using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using OhioState.AssetManaged;
using OhioState.Graphics.Proxy;


namespace OhioState.Controls.GLSL
{
    public partial class GLSLLinkerCtrl : UserControl
    {
        //
        // Pointer to the instance of the asset manager
        //
        private AssetManager assetMan;

        //
        // List of templates
        //
        private List<ShaderProgram> templates;

        //
        // Instance of ShaderProgram that is currently selected
        //
        private ShaderProgram shaderProgram;

        //
        // List of delegates to vertex and fragment asset manager controls changed events
        //
        private List<IAssetManagedListHandler> vertexDelegates;
        private List<IAssetManagedListHandler> fragmentDelegates;

        //
        // Link delegate and link event.  This is used to dispatch a message that the link
        //  button has been clicked.  At creation time the proper linkage should be done to set this up.
        //  This no only allows this control to decouple from an OpenGL functionality, but also allows
        //  remote clients to call link and have the link event occur on the server instead of the client.
        //
        public delegate bool LinkHandler(ShaderProgram shader);
        public LinkHandler LinkEvent;

        //
        // Constructor sets up all the object and events
        //
        public GLSLLinkerCtrl()
        {
            InitializeComponent();

            //
            // Get a pointer to the instance of the asset manager and create templates list
            //
            assetMan = AssetManager.Instance;
            templates = new List<ShaderProgram>();

            //
            // Set up event handler for selection changed event
            //
            vertexDelegates = new List<IAssetManagedListHandler>();
            fragmentDelegates = new List<IAssetManagedListHandler>();
            shaderProgramAssetManCtrl.SelectedEvent += new IAssetManagedListHandler(assetManagerControl1_SelectedEvent);

            //
            // Initialize the control
            //
            //shaderProgramAssetManCtrl.Init(typeof(ShaderProgram));
            List<Type> types1 = new List<Type>();
            types1.Add(typeof(ShaderProgram));
            shaderProgramAssetManCtrl.Init(types1);
            //vertexAssetManCtrl.Init(typeof(VertexShader));
            List<Type> types2 = new List<Type>();
            types2.Add(typeof(VertexShader));
            vertexAssetManCtrl.Init(types2);
            //fragmentAssetManCtrl.Init(typeof(FragmentShader));
            List<Type> types3 = new List<Type>();
            types3.Add(typeof(FragmentShader));
            fragmentAssetManCtrl.Init(types3);
            
            //
            // Create default transfer functions
            //
            SetupDefaultShaderProgram();

            //
            // Set up the drop down for the templates
            //
            BuiltTemplateDropDown();
        }

        #region Setup Default Shader Program
        //
        // Create a default shader program and load it into the asset manager
        //
        private void SetupDefaultShaderProgram()
        {
            VertexShader vertexRoutine;
            FragmentShader fragmentRoutine;
            //ShaderProgram program;
            List<VertexShader> vertexList = new List<VertexShader>();
            List<FragmentShader> fragmentList = new List<FragmentShader>();
            vertexRoutine = new VertexShader("defaultVertexShader", GLSLEditorCtrl.defaultVertShader);
            fragmentRoutine = new FragmentShader("defaultFragmentShader", GLSLEditorCtrl.defaultFragShader);
            vertexList.Add(vertexRoutine);
            fragmentList.Add(fragmentRoutine);
            shaderProgram = new ShaderProgram("defaultProgram", vertexList, fragmentList);
            templates.Add(shaderProgram);
        }
        #endregion

        //
        // Setup the drop down list of all templates
        //  Clear the Drop down menu and then add each item from the template list to the
        //  drop down menu.  Create preview icons as well.
        //
        private void BuiltTemplateDropDown()
        {
            newToolStripButton.DropDownItems.Clear();
            Image image = new Bitmap(32, 32); // global::GLSLControl.Properties.Resources.Text_icon;
            foreach (ShaderProgram obj in templates)
            {
                newToolStripButton.DropDownItems.Add(obj.Name, image, CreateNewFromTemplate);
            }
        }

        //
        // Gets a transfer function from the template list given its name
        //  or returns null if it doesn't exist.
        //
        private ShaderProgram GetTemplate(String name)
        {
            foreach (ShaderProgram program in templates)
            {
                if (program.Name == name)
                {
                    return program;
                }
            }
            return null;
        }

        //
        // Event handler for Template Drop down clicks
        //  Find the shader program that corresponds to the drop down item,
        //  clone it and add it to the asset manager.
        //
        public void CreateNewFromTemplate(Object sender, EventArgs e)
        {
            if (sender.GetType() == typeof(ToolStripMenuItem))
            {
                String name = ((ToolStripMenuItem)sender).Text;
                ShaderProgram prog = GetTemplate(name);
                if (prog != null)
                {
                    shaderProgram = prog.Clone();
                    IAssetManaged obj = AddProgramToAssetManager(shaderProgram);
                    // check for duplicate
                    if (obj != shaderProgram)
                    {
                        // select the item that already exists
                        shaderProgramAssetManCtrl.SelectOneItem(obj);
                    }
                }
             }
        }

        private bool routineListChanged = false;
        //
        // Event handler for selection changed event
        //  This changes the shader program
        // 
        public void assetManagerControl1_SelectedEvent_thisThread(IList<IAssetManaged> items)
        {
            //
            // Remove all delegates watching for selection changes to ensure they don't get modified
            //  during this transition of selections.
            //
            foreach (IAssetManagedListHandler item in vertexDelegates)
            {
                vertexAssetManCtrl.SelectedEvent -= item;
            }
            foreach (IAssetManagedListHandler item in fragmentDelegates)
            {
                fragmentAssetManCtrl.SelectedEvent -= item;
            }
            vertexDelegates.Clear();
            fragmentDelegates.Clear();

            //
            // Now we can clear the selections of the asset manager controls safely
            //
            vertexAssetManCtrl.ClearSelection();
            fragmentAssetManCtrl.ClearSelection();

            //
            // Polulate the selections with the the proper items from the first item selected
            //
            if (items.Count > 0)
            {
                shaderProgram = (ShaderProgram)items[0];

                for (int i = 0; i < shaderProgram.VertexListCount(); i++)
                {
                    VertexShader shader = shaderProgram.GetVertexShader(i);
                    if (!assetMan.Contains(shader))
                    {
                        assetMan.Add(shader);
                    }
                    vertexAssetManCtrl.SelectItem(shader);
                }

                for (int i = 0; i < shaderProgram.FragmentListCount(); i++)
                {
                    FragmentShader shader = shaderProgram.GetFragmentShader(i);
                    if (!assetMan.Contains(shader))
                    {
                        assetMan.Add(shader);
                    }
                    fragmentAssetManCtrl.SelectItem(shader);
                }
                //
                // Set the dirty bit for routine selection to false.
                //
                routineListChanged = false;
                //
                // Reassign the delegates to watch for changes in shader selections
                //
                //foreach (IAssetManaged obj in items)
                //{
                //    ShaderProgram prog = (ShaderProgram)obj;

                //IAssetManagedListHandler newHandler = new IAssetManagedListHandler(shaderProgram.vertexListChanged);
                //vertexAssetManCtrl.SelectedEvent += newHandler;
                //vertexDelegates.Add(newHandler);

                //newHandler = new IAssetManagedListHandler(shaderProgram.fragmentListChanged);
                //fragmentAssetManCtrl.SelectedEvent += newHandler;
                //fragmentDelegates.Add(newHandler);
                //}

                //logInfoRichTextBox.Clear();
                logInfoRichTextBox.Text = shaderProgram.LinkInfo;
            }
        }

        public void assetManagerControl1_SelectedEvent(IList<IAssetManaged> items)
        {
            IAssetManagedListHandler newEvent = new IAssetManagedListHandler(assetManagerControl1_SelectedEvent_thisThread);
            object[] args = new object[1];
            args[0] = items;
            //
            // We need to make sure these methods are executed on the correct thread.
            //
            try
            {
                if (this.InvokeRequired)
                    this.Invoke(newEvent, args);
                else
                {
                //    List<IAssetManaged> assets = new List<IAssetManaged>();
                //    foreach (IAssetManaged obj in items)
                //        assets.Add(obj);
                //    newEvent(assets);
                    assetManagerControl1_SelectedEvent_thisThread(items);
                }
            }
            catch { }
        }

        //
        // New button - Create a new shader program
        //
        private void newButton_Click(object sender, EventArgs e)
        {
            logInfoRichTextBox.Clear();
            if (shaderProgramAssetManCtrl.GetSelected().Count > 0)
            {
                shaderProgram = ((ShaderProgram)shaderProgramAssetManCtrl.GetSelected()[0]).Clone();
                shaderProgram.SetName(shaderProgram.Name + " Copy");
            }
            else
            {
                shaderProgram = new ShaderProgram("Untitled");
            }
            IAssetManaged obj = assetMan.Add(shaderProgram);
        }

        //
        // Open button - calls a dialog to open a file that contains a shader program,
        //  and adds it to the asset manager.  If there is a name,type collision then the new
        //  object does not get added.  Instead the object that already exists gets selected
        //  Also, load shader routines belonging to the shader program into list boxes
        //
        private void openButton_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                shaderProgram = ShaderProgram.LoadFromFile(openFileDialog.FileName);
                if (shaderProgram != null)
                {
                    IAssetManaged obj = AddProgramToAssetManager(shaderProgram);
                    // check for duplicate
                    if (obj == shaderProgram)
                    {
                        // add the new item to the template list and the template drop down if it doesn't
                        //  already exist
                        // Also, bring all shader routines belonging to the shader program
                        if (GetTemplate(shaderProgram.Name) == null)
                        {
                            templates.Add((ShaderProgram)obj);
                            BuiltTemplateDropDown();
                            logInfoRichTextBox.Clear();
                            logInfoRichTextBox.Text = shaderProgram.LinkInfo;
                        }
                    }
                    else
                    {
                        // select the item that already exists
                        shaderProgramAssetManCtrl.SelectOneItem(obj);
                    }
                }
            }
        }

        //
        // Save button - saves the currently selected shader program to a file.
        //
        private void saveButton_Click(object sender, EventArgs e)
        {
            if (shaderProgramAssetManCtrl.GetSelected().Count > 0)
            {
                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    shaderProgram.SaveToFile(saveFileDialog.FileName);
                }
            }
        }

        //
        // Remove button - remove the currently selected shader program from the asset
        //  manager. 
        //
        private void removeButton_Click(object sender, EventArgs e)
        {
            List<IAssetManaged> list = shaderProgramAssetManCtrl.GetSelected();
            foreach (IAssetManaged obj in list)
            {
                assetMan.Remove(obj);
            }
            if (list != null)
                logInfoRichTextBox.Clear();
        }

        //
        // Link button - link selected shader routines to the selected shader program(s)
        //
        private void linkButton_Click(object sender, EventArgs e)
        {
            logInfoRichTextBox.Clear();
            // only one shader program selected
            //if (shaderProgramAssetManCtrl.GetSelected().Count == 1)
            //{
            //    //shaderProgram.Link();
            //    GLShader.Instance.Link(shaderProgram);
            //    logInfoRichTextBox.Text = shaderProgram.LinkInfo;
            //}
            // multiple programs selected
            //else
            if (shaderProgramAssetManCtrl.GetSelected().Count > 0)
            {
                ShaderProgram program = shaderProgramAssetManCtrl.GetSelected()[0] as ShaderProgram;
                if( program != null )
                {
                    //program.Link();
                    if (routineListChanged)
                    {
                        program.BeginManipulation();
                        IList<IAssetManaged> vertexRoutines = vertexAssetManCtrl.GetSelected();
                        program.vertexListChanged(vertexRoutines);
                        IList<IAssetManaged> fragmentRoutines = fragmentAssetManCtrl.GetSelected();
                        program.fragmentListChanged(fragmentRoutines);
                        program.EndManipulation();
                        routineListChanged = false;
                    }
                    //
                    // Use delegates with Invoke to call link
                    //
                    if (LinkEvent != null)
                    {
                        LinkEvent.Invoke(program);
                        // This field should be populated now
                        logInfoRichTextBox.Text += program.LinkInfo + "\n\n";
                    }
                }
            }
            else
            {
                logInfoRichTextBox.Text = "No Selected Program";
            }
        }

        #region Helper Methods

        //
        // Add program to the asset manager and make sure its sub routines are added as well.
        //  We need this for when we add from a template or a file where the program will already
        //  contain shader routines that it wants to use.
        // To ensure that the routines get added, we add a number to the end of the name if there is a name
        //  conflict and try to add them again.  We increment this number until we get a successful 
        //  add.
        //
        private ShaderProgram AddProgramToAssetManager(ShaderProgram prog)
        {
            for (int i = 0; i < prog.VertexListCount(); i++)
            {
                VertexShader shader = prog.GetVertexShader(i);assetMan.Add(shader);
            }

            for (int i = 0; i < prog.FragmentListCount(); i++)
            {
                FragmentShader shader = prog.GetFragmentShader(i);
                assetMan.Add(shader);
            }

            return (ShaderProgram)assetMan.Add(prog);
        }

        #endregion

        private void routinesSelectedEvent(IList<IAssetManaged> items)
        {
            routineListChanged = true;
        }

        private void routinesSelectedEvent()
        {

        }
    }
}


